//
//  ZipManager.swift
//  ToBeBigFE
//
//  Created by mingo on 2020/3/5.
//  Copyright © 2020 com.example. All rights reserved.
//

import Foundation
import Alamofire
import ZIPFoundation


typealias OfflineZipUpdatedCallback = (_ error: ZipError?) -> Void

enum ZipError : Error {
    case argumentError
    case downloadFail
    case unzipFail
}

class ZipManager {
    
    private let unzipOkFileName = "unzipOK.file"
    private let unzipBasePath: NSString

    private var zipUpdatingCallbackDict = [String:[OfflineZipUpdatedCallback]]()
    
    init() {
        let documentDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
        unzipBasePath = (documentDir as NSString).appendingPathComponent("Web") as NSString
        
        let fileMgr = FileManager.default
        var isDir: ObjCBool = false
        if (!fileMgr.fileExists(atPath: unzipBasePath as String, isDirectory: &isDir) || !isDir.boolValue) {
            try! fileMgr.createDirectory(atPath: unzipBasePath as String, withIntermediateDirectories: true, attributes: nil)
        }
        
        var unzipBaseURL = URL(fileURLWithPath: unzipBasePath as String)
        var resourceValues = URLResourceValues()
        resourceValues.isExcludedFromBackup = true
        try! unzipBaseURL.setResourceValues(resourceValues)
    }

    func hasOfflineFiles(id: String, version: String) -> Bool {
        let filePath = getOfflineFile(id: id, version: version, filePath: unzipOkFileName)
        var isDir: ObjCBool = false
        let isExists = FileManager.default.fileExists(atPath: filePath, isDirectory: &isDir)
        return isExists && !isDir.boolValue
    }

    func getOfflineFile(id: String, version: String, filePath: String) -> String {
        return unzipBasePath.appendingPathComponent("\(id)/\(version)/\(filePath)")
    }

    func unzipBuiltin(id: String, version: String) {
        print("ZipManager unzip builtin \(id) \(version)")
        let fileMgr = FileManager.default
        let builtinZipURL = Bundle.main.url(forResource: id, withExtension: "zip")!
        let destPath = unzipBasePath.appendingPathComponent("\(id)/\(version)")
        if fileMgr.fileExists(atPath: destPath) {
            try! fileMgr.removeItem(atPath: destPath)
        }
        try? fileMgr.unzipItem(at: builtinZipURL, to: URL(fileURLWithPath: destPath))
        let unzipOkFilePath = (destPath as NSString).appendingPathComponent(unzipOkFileName)
        fileMgr.createFile(atPath: unzipOkFilePath, contents: nil, attributes: nil)
    }
    
    func updateOfflineZip(id: String, version: String, callback: @escaping OfflineZipUpdatedCallback) {
        if (hasOfflineFiles(id: id, version: version)) {
            callback(nil)
            return
        }

        if var callbackList = zipUpdatingCallbackDict[id] {
            callbackList.append(callback)
            return
        }

        print("ZipManager update zip")

        var callbackList = [OfflineZipUpdatedCallback]()
        callbackList.append(callback)
        zipUpdatingCallbackDict[id] = callbackList

        let fileMgr = FileManager.default
        var isDir: ObjCBool = false
        if (!fileMgr.fileExists(atPath: unzipBasePath as String, isDirectory: &isDir) || !isDir.boolValue) {
            try! fileMgr.createDirectory(atPath: unzipBasePath as String, withIntermediateDirectories: true, attributes: nil)
        }

        let zipUrl = "\(WebConst.WEB_BASE_URL)\(id)/\(version)/\(id).zip"

        print("ZipManager download \(zipUrl)")
        
        let callbackAll = { (error: ZipError?) in
            callbackList.forEach { callback in
                callback(error)
            }
            self.zipUpdatingCallbackDict.removeValue(forKey: id)
            print("ZipManager download complete. \(error == nil ? "" : error.debugDescription)")
        }

        AF.download(zipUrl).response { resp in
            if let _ = resp.error {
                callbackAll(.downloadFail)
                return
            }
            guard let fileURL = resp.fileURL else {
                callbackAll(.downloadFail)
                return
            }
            

            let destPath = self.unzipBasePath.appendingPathComponent("\(id)/\(version)")
            let destURL = URL(fileURLWithPath: destPath)
            do {
                if fileMgr.fileExists(atPath: destPath) {
                    try fileMgr.removeItem(atPath: destPath)
                }
                try fileMgr.unzipItem(at: fileURL, to: destURL)
                let unzipOkFilePath = (destPath as NSString).appendingPathComponent(self.unzipOkFileName)
                FileManager.default.createFile(atPath: unzipOkFilePath, contents: nil, attributes: nil)
                callbackAll(nil)
            } catch {
                callbackAll(.unzipFail)
            }
        }
    }
}
